import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
import { ElMessage } from 'element-plus';
import { h } from 'vue';
import clipboard3 from 'vue-clipboard3';

import { CommonResponse, HTTPMethod, RspStatusCode } from '@/types/common.type';

interface ErrorType {
  data: string;
  time: number;
}

declare global {
  interface Window {
    rawWindow: any;
    __MICRO_APP_NAME__: string;
    __MICRO_APP_ENVIRONMENT__: string;
    __MICRO_APP_BASE_ROUTE__: string;
  }
}

const { toClipboard } = clipboard3();

class HTTP {
  static singletonHttpInstance: HTTP | null = null;

  private baseURL: string = import.meta.env.VITE_BASE_HOST;

  private defaultTimeout: number = import.meta.env.VITE_REQ_TIMEOUT;

  private errorList: Array<ErrorType> = []; // 错误收集集合

  private defaultHeader = {
    contentType: 'application/json',
  };

  constructor() {
    if (HTTP.singletonHttpInstance) {
      return HTTP.singletonHttpInstance;
    }

    axios.defaults.withCredentials = true;

    /** 请求拦截器  */
    axios.interceptors.request.use(
      (config: any) => {
        config.headers = {
          Jtzc_Grey: 'cwgl_v2',
        };
        return config;
      },
      (error) => {
        return Promise.reject(error);
      }
    );

    /** 响应拦截器  */
    axios.interceptors.response.use(
      (response: AxiosResponse) => {
        // 导出文件走 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8-->
        if (response.headers['content-type'] === 'application/octet-stream;charset=utf-8') {
          console.log('response', response);
          return response;
        }
        if (response.data && [RspStatusCode.SUCCESS, '0000'].includes(response.data.code)) {
          return response;
        }
        // 未登录
        if (response.data.code === '401') {
          // 登录页 -- 不需要重定向
          // alert(location.href + '/' + location.origin + import.meta.env.VITE_SIGN_PATH);
          if (
            location.pathname === import.meta.env.VITE_SIGN_PATH ||
            location.origin + location.pathname === import.meta.env.VITE_SIGN_PATH
          ) {
            console.log('登录页不需要再次重定向');
          } else {
            const locationHref = window?.rawWindow ? window.rawWindow.location.href : window.location.href;
            window.location.replace(
              // import.meta.env.VITE_SIGN_PATH + '?redirect=' + encodeURIComponent(location.origin + location.pathname),
              import.meta.env.VITE_SIGN_PATH + '?redirect=' + encodeURIComponent(locationHref)
            );
          }
        }
        // 未登录
        if (response.data.code === '403') {
          ElMessage.error('暂无访问权限，请联系客服');
          return response;
        }
        // 二进制请求报错处理
        if (response.config.responseType === 'arraybuffer') {
          // 创建一个TextDecoder对象
          const decoder = new TextDecoder('utf8');
          response.data = JSON.parse(decoder.decode(response.data));
          if (response.data && [RspStatusCode.SUCCESS, '0000'].includes(response.data.code)) {
            return response;
          }
        }

        // 5xx、404
        if (response.data.code.startsWith('5') || response.data.code === '404') {
          // rootRoutes.push({
          //   path: '/500',
          //   query: {
          //     id: response.data.code,
          //   },
          // });
        }
        return Promise.reject(response.data);
      },
      (error: AxiosError) => {
        // if (error.response?.status === 401) {
        //   if (location.href != location.origin + import.meta.env.VITE_SIGN_PATH) {
        //     window.location.replace(
        //       // import.meta.env.VITE_SIGN_PATH + '?redirect=' + encodeURIComponent(location.origin + location.pathname),
        //       import.meta.env.VITE_SIGN_PATH + '?redirect=' + encodeURIComponent(location.href),
        //     );
        //   } else {
        //     window.location.replace(import.meta.env.VITE_SIGN_PATH);
        //   }
        // }
        return Promise.reject(error);
      }
    );

    HTTP.singletonHttpInstance = this;
    return HTTP.singletonHttpInstance;
  }

  /**
   *
   * @param url 请求地址
   * @param method 请求方法
   * @param data 请求体
   * @param header 请求头
   * @param timeout 请求超时时间
   * @returns {Promise<AxiosResponse<T>>}
   */
  public sendReq<T>(
    url: string,
    method: HTTPMethod,
    data?: { [key: string]: any },
    option?: AxiosRequestConfig,
    isNeedErrorMsg = true
  ): Promise<null | CommonResponse<T>> {
    const sendUrl = `${option?.baseURL || this.baseURL}${url}`;

    const currentTimeout: number = this.defaultTimeout;
    const currentHeader = this.defaultHeader;
    let responsePromise: Promise<null | CommonResponse<T>> = new Promise(() => null);

    const config = {
      headers: { ...currentHeader, ...option?.headers },
      timeout: option?.timeout || currentTimeout,
      responseType: option?.responseType,
      onUploadProgress: option?.onUploadProgress,
    };
    switch (method) {
      case HTTPMethod.GET: {
        responsePromise = axios
          .get<CommonResponse<T>>(sendUrl, {
            ...config,
            params: data,
          })
          .then((response) => {
            const data =
              response.headers['content-type'] === 'application/octet-stream;charset=utf-8'
                ? ({
                    data: { data: response.data, headers: response.headers },
                  } as any)
                : response.data;
            return data;
          })
          .catch((error: any) => {
            this._catchError({ apiPath: url, ...error }, isNeedErrorMsg);
            throw error;
          });
        break;
      }
      case HTTPMethod.DELETE: {
        responsePromise = axios
          .delete<CommonResponse<T>>(sendUrl, {
            ...config,
            params: data,
          })
          .then((response) => {
            const data =
              response.headers['content-type'] === 'application/octet-stream;charset=utf-8'
                ? ({
                    data: { data: response.data, headers: response.headers },
                  } as any)
                : response.data;
            return data;
          })
          .catch((error: any) => {
            this._catchError({ apiPath: url, ...error }, isNeedErrorMsg);
            throw error;
          });
        break;
      }
      case HTTPMethod.PUT:
      case HTTPMethod.POST: {
        responsePromise = axios
          .post<CommonResponse<T>>(sendUrl, data, {
            ...config,
          })
          .then((response) => {
            const data =
              response.headers['content-type'] === 'application/octet-stream;charset=utf-8'
                ? ({
                    data: { data: response.data, headers: response.headers },
                  } as any)
                : response.data;
            return data;
          })
          .catch((error: any) => {
            this._catchError({ apiPath: url, ...error }, isNeedErrorMsg);
            throw error;
          });
        break;
      }
      default: {
        break;
      }
    }
    return responsePromise;
  }

  /**
   *
   * @param val 错误对象
   * @returns {void}
   */
  private _hasErrorData(val: ErrorType) {
    let flag = false;
    // 只判断2秒之内的错误
    this.errorList = this.errorList.filter((_: ErrorType) => Date.now() - _.time < 2000);
    for (const error of this.errorList) {
      if (val.data === error.data) {
        flag = true;
        break;
      }
    }
    return flag;
  }

  /**
   *
   * @param error 错误信息
   * @param isNeedErrorMsg 是否需要显示错误消息
   * @returns {void}
   */
  private _catchError(error: any, isNeedErrorMsg = true) {
    let data = '服务异常';
    data = typeof error === 'string' ? error || '服务异常' : error.message || error.msg || '服务异常';
    const copyJson = {
      tarceInfo: error.traceInfo,
      apiPath: error.apiPath,
      msg: error.msg,
      menuPath: window.location.href,
    };
    if (!this._hasErrorData({ data, time: Date.now() }) && isNeedErrorMsg) {
      this.errorList.push({ data, time: Date.now() });
      ElMessage({
        type: 'error',
        message: h('span', {}, [
          h('span', {}, data.replaceAll('<enter>', '\n\r')),
          h(
            'a',
            {
              style: {
                color: '#1890ff',
                marginLeft: '10px',
                cursor: 'pointer',
              },
              onClick: async () => {
                await toClipboard(JSON.stringify(copyJson));
                ElMessage.success('复制成功');
              },
            },
            '复制'
          ),
        ]),
        duration: 3000,
      });
    }
  }
}

export default new HTTP();
